﻿#pragma once

#include "Core/Camera.h"
#include "Render/VertexArray.h"
#include "Render/Shader.h"
#include "Render/Atlas.h"
#include "World/BlockData.h"
#include "World/BlockLight.h"
#include "Physics/AABB.h"

// lib
#include <glm/glm.hpp>

class World;

class Chunk
{
public:
  static constexpr int HorizontalSize = 16;
  static constexpr int VerticalSize = 256;

  static constexpr int BlockCount = HorizontalSize * HorizontalSize * VerticalSize;
  static constexpr int MaxVertexCount = BlockCount * 6 * 6;

  Chunk(const glm::ivec2 &worldPosition);

  void Init();

  bool NeedsRebuildMesh() const { return !m_mesh || m_renderState != RenderState::Ready; }
  void RebuildMesh(const World &world);

  void SetShader(const Ref<Shader> &shader) { m_shader = shader; };
  void SetAtlas(const Ref<Atlas> &atlas) { m_atlas = atlas; };
  void SetDirty() { m_renderState = RenderState::Dirty; }

  void SetUseAmbientOcclusion(bool enabled)
  {
    if (enabled == m_useAmbientOcclusion)
    {
      return;
    }

    SetDirty();
    m_useAmbientOcclusion = enabled;
  };

  static bool IsValidPosition(const glm::ivec3 &chunkPosition);

  bool PlaceBlock(const glm::ivec3 &chunkPosition, BlockData block);
  BlockData *GetBlockAt(const glm::ivec3 &chunkPosition);

  const glm::ivec2 &GetPosition() const { return m_worldPosition; }
  float Distance2DTo(const glm::vec2 &point) const;

  void RenderOpaque(const Ref<Camera> &camera);
  void RenderSemiTransparent(const Ref<Camera> &camera);

  const BlockData *GetBlockAtOptimized(const glm::ivec3 &pos, const World &world) const;

  BlockData data[HorizontalSize][VerticalSize][HorizontalSize];
  uint8_t vertexLightData[HorizontalSize][VerticalSize][HorizontalSize];
  std::vector<LightSource> lightSources;

private:
  enum class RenderState
  {
    Init,
    Ready,
    Dirty
  };

  Ref<VertexArray> m_mesh;
  Ref<Shader> m_shader;
  Ref<Atlas> m_atlas;

  RenderState m_renderState;
  glm::ivec2 m_worldPosition;

  int m_solidVertexCount;
  int m_semiTransparentVertexCount;

  bool m_useAmbientOcclusion = true;

  AABB m_aabb;
};